/* global Canvg, describe, it, xit, jsPDF, comparePdf, loadBinaryResource, loadGlobals, isNode */
/**
 * Standard spec tests
 */

describe("Context2D: standard tests", () => {
  beforeAll(loadGlobals);
  if (typeof isNode === "undefined" || !isNode) {
    it("bar_graph_with_text_and_lines - direct svg", async () => {
      const svg = loadBinaryResource(
        "reference/bar_graph_with_text_and_lines.svg"
      );
      var doc = new jsPDF({
        orientation: "p",
        unit: "pt",
        format: "c1",
        floatPrecision: 3
      });
      var c = doc.canvas;
      c.width = 1000;
      c.height = 500;

      var ctx = c.getContext("2d");
      ctx.ignoreClearRect = true;
      ctx.fillStyle = "#ffffff";
      ctx.fillRect(0, 0, 1000, 700);

      //load a svg snippet in the canvas with id = 'drawingArea'
      const options = {
        ignoreMouse: true,
        ignoreAnimation: true,
        ignoreDimensions: true
      };
      const canvg = await Canvg.fromString(ctx, svg, options);
      await canvg.render(options);
      comparePdf(
        doc.output(),
        "bar_graph_with_text_and_lines.pdf",
        "context2d"
      );
    });

    it("piechart - direct svg", async () => {
      const svg = loadBinaryResource("reference/piechart.svg");
      var doc = new jsPDF({
        orientation: "p",
        unit: "pt",
        format: "c1",
        floatPrecision: 3
      });
      var c = doc.canvas;
      c.width = 1000;
      c.height = 500;

      var ctx = c.getContext("2d");
      ctx.ignoreClearRect = true;
      ctx.fillStyle = "#ffffff";
      ctx.fillRect(0, 0, 1000, 700);

      //load a svg snippet in the canvas with id = 'drawingArea'
      const options = {
        ignoreMouse: true,
        ignoreAnimation: true,
        ignoreDimensions: true
      };
      const canvg = await Canvg.fromString(ctx, svg, options);
      await canvg.render(options);

      comparePdf(doc.output(), "piechart.pdf", "context2d");
    });
  }

  it("context2d: paths", () => {
    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "letter",
      floatPrecision: 2
    });
    var context = doc.context2d;

    context.arc(150, 150, 50, 0, Math.PI, false);
    context.lineTo(300, 300);
    context.stroke();

    context.fillStyle = "red";
    context.fillRect(545, 205, 50, 40);

    comparePdf(doc.output(), "paths.pdf", "context2d");
  });

  it("context2d: fonts", () => {
    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 2
    });

    var ctx = doc.context2d;
    var y = 0;
    var pad = 10;
    var textHeight = 20;

    ctx.font = "10pt times";
    ctx.fillText("10pt times", 20, y + textHeight);
    y += textHeight + pad;

    ctx.font = "10pt courier";
    ctx.fillText("10pt courier", 20, y + textHeight);
    y += textHeight + pad;

    ctx.font = "bold 10pt courier";
    ctx.fillText("bold 10pt courier", 20, y + textHeight);
    y += textHeight + pad;

    ctx.font = "italic 10pt courier";
    ctx.fillText("10pt courier italic", 20, y + textHeight);
    y += textHeight + pad;

    ctx.font = "italic bold 10pt courier";
    ctx.fillText("italic bold 10pt courier", 20, y + textHeight);
    y += textHeight + pad;

    ctx.font = "bold 50pt courier";
    ctx.fillText("bold 50pt courier", 20, y + 50);
    y += 50 + pad;

    ctx.font = "bold 50pt courier";
    ctx.strokeText("bold 50pt courier", 20, y + 50);
    y += 50 + pad;

    ctx.font = "bold 20pt courier";
    ctx.strokeText("bold 20pt courier", 20, y + 20);
    y += 20 + pad;

    ctx.font = "bold 20pt courier";
    ctx.fillText("bold 20pt courier", 20, y + 20);
    y += 20 + pad;

    ctx.font = "bold 20pt arial";
    ctx.fillText("bold 20pt arial", 20, y + 20);
    y += 20 + pad;

    comparePdf(doc.output(), "fonts.pdf", "context2d");
  });

  it("context2d: custom fonts", () => {
    var PTSans = loadBinaryResource("reference/PTSans.ttf");

    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 2
    });
    doc.addFileToVFS("PTSans.ttf", PTSans);
    doc.addFont("PTSans.ttf", "PTSans", "normal");

    var ctx = doc.context2d;
    ctx.font = "normal 16pt PTSans";
    ctx.fillText("А ну чики брики и в дамки!", 20, 20);

    comparePdf(doc.output(), "context2d-custom-fonts.pdf", "context2d");
  });

  it("context2d: custom fonts name regex", () => {
    const FaSolid = loadBinaryResource("reference/fa-solid-900.ttf");

    const doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 2
    });
    doc.addFileToVFS("fa-solid-900.ttf", FaSolid);
    doc.addFont("fa-solid-900.ttf", "Font Awesome 6 Free Solid", "normal");

    const ctx = doc.context2d;
    ctx.font = 'normal 16pt "Font Awesome 6 Free Solid"';

    expect(doc.getFont().fontName).toBe("Font Awesome 6 Free Solid");
  });

  it("context2d: css color names", () => {
    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 2
    });
    var ctx = doc.context2d;

    var y = 0;
    var pad = 10;
    var textHeight = 20;
    ctx.font = "20pt times";

    ctx.save();
    y += textHeight + pad;

    ctx.fillStyle = "red";
    ctx.fillText("Red", 20, y + textHeight);
    y += textHeight + pad;

    ctx.fillStyle = "green";
    ctx.fillText("Green", 20, y + textHeight);
    y += textHeight + pad;

    ctx.strokeStyle = "blue";
    ctx.strokeText("Blue", 20, y + textHeight);
    y += textHeight + pad;
    ctx.restore();

    comparePdf(doc.output(), "csscolornames.pdf", "context2d");
  });

  it("context2d: drawImage", () => {
    var imgData =
      "";

    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 2
    });
    var ctx = doc.context2d;

    var y = 0;
    var pad = 20;
    y += pad;
    ctx.drawImage(imgData, 20, y, 50, 50);
    y += 50 + pad;
    ctx.drawImage(imgData, 20, y, 200, 200);
    y += 200 + pad;
    ctx.drawImage(imgData, 20, y, 400, 400);
    comparePdf(doc.output(), "drawImage.pdf", "context2d");
  });

  it("context2d: curves", () => {
    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 3
    });
    var ctx = doc.context2d;

    var y = 0;
    var pad = 20;

    y = pad;

    ctx.save();
    ctx.lineWidth = 6;
    ctx.strokeStyle = "#333";
    ctx.beginPath();
    ctx.moveTo(100, y);
    ctx.bezierCurveTo(150, y + 100, 350, y + 100, 400, y);
    ctx.stroke();
    ctx.restore();

    y += 100 + pad;
    ctx.save();
    ctx.lineWidth = 6;
    ctx.strokeStyle = "#333";
    ctx.beginPath();
    ctx.moveTo(100, y);
    ctx.bezierCurveTo(150, y + 100, 350, y + 100, 400, y);
    ctx.fill();
    ctx.restore();

    y += 100 + pad;
    ctx.save();
    ctx.lineWidth = 6;
    ctx.strokeStyle = "#333";
    ctx.beginPath();
    ctx.moveTo(100, y);
    ctx.quadraticCurveTo(250, y + 100, 400, y);
    ctx.stroke();
    ctx.restore();

    y += 100 + pad;
    ctx.save();
    ctx.lineWidth = 6;
    ctx.strokeStyle = "#333";
    ctx.beginPath();
    ctx.moveTo(100, y);
    ctx.quadraticCurveTo(250, y + 100, 400, y);
    ctx.fill();
    ctx.restore();
    comparePdf(doc.output(), "curves.pdf", "context2d");
  });

  it("context2d: strokeStyle and fillstyle", () => {
    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 3
    });
    var ctx = doc.context2d;

    var y = 0;
    var h = 20;
    var pad = 20;

    y = pad;

    // test fill style
    ctx.fillStyle = "#ff0000";
    ctx.fillRect(20, y, 20, h);
    y += h + pad;

    ctx.fillStyle = "#00ff00";
    ctx.fillRect(20, y, 20, h);
    y += h + pad;

    ctx.fillStyle = "#0000ff";
    ctx.fillRect(20, y, 20, h);
    y += h + pad;

    // test stroke style
    ctx.strokeStyle = "#ff0000";
    ctx.strokeRect(20, y, 20, h);
    y += h + pad;

    ctx.strokeStyle = "#00ff00";
    ctx.strokeRect(20, y, 20, h);
    y += h + pad;

    ctx.strokeStyle = "#0000ff";
    ctx.strokeRect(20, y, 20, h);
    y += h + pad;

    comparePdf(doc.output(), "fillStyle_strokeStyle.pdf", "context2d");
  });

  it("context2d: arc", () => {
    var doc = new jsPDF(
      {
        floatPrecision: 2
      },
      "pt",
      "a4"
    );
    var ctx = doc.context2d;

    var y = 0;
    var pad = 20;

    ctx.strokeStyle = "black";
    ctx.fillStyle = "black";

    y = pad + 40;
    ctx.beginPath();
    ctx.arc(50, y, 20, -Math.PI / 3, Math.PI, false);
    ctx.stroke();
    y += pad + 40;

    ctx.beginPath();
    ctx.arc(50, y, 20, -Math.PI / 3, Math.PI, true);
    ctx.stroke();
    y += pad + 40;

    ctx.beginPath();
    ctx.arc(50, y, 20, 0, Math.PI, false);
    ctx.stroke();
    y += pad + 40;

    ctx.beginPath();
    ctx.arc(50, y, 20, 0, Math.PI, true);
    ctx.stroke();
    y += pad + 40;

    ctx.beginPath();
    ctx.arc(50, y, 20, 0, 2 * Math.PI, false);
    ctx.stroke();
    y += pad + 40;

    ctx.beginPath();
    ctx.arc(50, y, 20, 0, 2 * Math.PI, false);
    ctx.fill();
    y += pad + 40;

    ctx.beginPath();
    ctx.arc(50, y, 20, 0, Math.PI, false);
    ctx.fill();
    y += pad + 40;

    ctx.beginPath();
    ctx.arc(50, y, 20, 0, Math.PI);
    ctx.closePath();
    ctx.stroke();
    y += pad + 40;

    ctx.beginPath();
    ctx.arc(50, y, 20, -Math.PI / 3, Math.PI, true);
    ctx.closePath();
    ctx.stroke();

    ctx.lineWidth = 4;
    ctx.strokeStyle = "red";
    y = 80;
    ctx.beginPath();
    ctx.moveTo(150, y);
    ctx.lineTo(150, y + 35);
    ctx.arc(150, y, 65, 0, Math.PI * 0.8);
    ctx.closePath();
    ctx.fill();
    ctx.stroke();

    y = 160;
    ctx.beginPath();
    ctx.moveTo(150, y);
    ctx.arc(150, y, 65, 0, Math.PI * 0.8);
    ctx.fill();
    ctx.stroke();

    y = 280;
    ctx.beginPath();
    ctx.moveTo(150, y);
    ctx.arc(150, y, 30, 0, 2 * Math.PI);
    ctx.fill();
    ctx.stroke();

    comparePdf(doc.output(), "arc.pdf", "context2d");
  });

  it("context2d: fillRect and strokeRect", () => {
    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 3
    });
    var ctx = doc.context2d;

    var y = 20;
    var h = 20;
    var pad = 20;

    ctx.fillRect(20, y, 20, h);
    y += h + pad;

    ctx.fillStyle = "#f5f5f5";
    ctx.fillRect(20, y, 20, h);
    y += h + pad;

    ctx.strokeRect(20, y, 20, h);
    y += h + pad;

    comparePdf(doc.output(), "fillRect_strokeRect.pdf", "context2d");
  });

  xit("context2d: setLineCap", () => {
    var doc = new jsPDF("p", "pt", "a4");
    var ctx = doc.context2d;

    var y = 20;
    var pad = 20;

    ctx.lineWidth = 5;
    ctx.lineCap = "butt";
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();
    y += pad;
    ctx.lineCap = "round";
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();
    y += pad;
    ctx.lineCap = "square";
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();
    y += pad;

    comparePdf(doc.output(), "lineCap.pdf", "context2d");
  });

  xit("context2d: lineJoin", () => {
    var doc = new jsPDF("p", "pt", "a4");
    var ctx = doc.context2d;

    var y = 20;
    var pad = 20;

    ctx.beginPath();
    ctx.lineWidth = 10;
    ctx.lineJoin = "miter";
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.lineTo(250, y + 50);
    ctx.stroke();
    ctx.closePath();
    y += pad + 10;
    ctx.beginPath();
    ctx.lineJoin = "bevel";
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.lineTo(250, y + 50);
    ctx.stroke();
    ctx.closePath();
    y += pad + 10;
    ctx.beginPath();
    ctx.lineJoin = "round";
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.lineTo(250, y + 50);
    ctx.stroke();
    ctx.closePath();
    y += pad + 10;
    y += 50;

    comparePdf(doc.output(), "lineJoin.pdf", "context2d");
  });

  it("context2d: moveTo, lineTo, stroke, fill", () => {
    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 3
    });
    var ctx = doc.context2d;

    var y = 20;

    // stroked diamond
    ctx.beginPath();
    ctx.moveTo(30, y);
    ctx.lineTo(50, y + 20);
    ctx.lineTo(30, y + 40);
    ctx.lineTo(10, y + 20);
    ctx.lineTo(30, y);
    ctx.stroke();
    ctx.closePath();
    y += 50;

    // filled diamond
    ctx.beginPath();
    ctx.moveTo(30, y);
    ctx.lineTo(50, y + 20);
    ctx.lineTo(30, y + 40);
    ctx.lineTo(10, y + 20);
    ctx.lineTo(30, y);
    ctx.fill();
    ctx.closePath();
    y += 50;
    comparePdf(doc.output(), "moveTo_lineTo_stroke_fill.pdf", "context2d");
  });

  it("context2d: setLineDash(), lineDashOffset", () => {
    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 2
    });
    var ctx = doc.context2d;

    var y = 20;
    var pad = 20;

    ctx.lineWidth = 5;
    ctx.beginPath();
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();
    y += pad;
    ctx.save();
    ctx.beginPath();
    ctx.setLineDash([10, 20]);
    ctx.lineDashOffset = 10;
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();
    y += pad;
    ctx.beginPath();
    ctx.setLineDash([]);
    ctx.lineDashOffset = 0;
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();
    y += pad;
    ctx.beginPath();
    ctx.setLineDash([10, 20]);
    ctx.lineDashOffset = 10;
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();
    y += pad;
    ctx.save();
    ctx.beginPath();
    ctx.setLineDash([]);
    ctx.lineDashOffset = 0;
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();
    y += pad;
    ctx.restore();
    ctx.beginPath();
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();
    y += pad;
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();
    y += pad;
    ctx.restore();
    ctx.beginPath();
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();
    y += pad;
    ctx.restore();
    ctx.beginPath();
    ctx.moveTo(20, y);
    ctx.lineTo(200, y);
    ctx.stroke();

    comparePdf(doc.output(), "lineDash.pdf", "context2d");
  });

  it("context2d: getLineDash()", () => {
    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 2
    });
    var ctx = doc.context2d;

    expect(ctx.getLineDash()).toEqual([]);

    ctx.setLineDash([1, 2]);
    expect(ctx.getLineDash()).toEqual([1, 2]);

    ctx.setLineDash([1, 2, 3]);
    expect(ctx.getLineDash()).toEqual([1, 2, 3, 1, 2, 3]);
  });

  it("context2d: textBaseline", () => {
    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 3
    });
    var ctx = doc.context2d;

    var y = 20;
    var pad = 20;

    ctx.strokeStyle = "#dddddd";
    ctx.font = "20pt times";

    ctx.moveTo(20, y);
    ctx.lineTo(150, y);
    ctx.stroke();
    ctx.textBaseline = "alphabetic";
    ctx.fillText("Alphabetic Q", 20, y);
    y += 40 + pad;

    ctx.moveTo(20, y);
    ctx.lineTo(150, y);
    ctx.stroke();
    ctx.textBaseline = "ideographic";
    ctx.fillText("Ideographic Q", 20, y);
    y += 40 + pad;

    ctx.moveTo(20, y);
    ctx.lineTo(150, y);
    ctx.stroke();
    ctx.textBaseline = "top";
    ctx.fillText("Top Q", 20, y);
    y += 40 + pad;

    ctx.moveTo(20, y);
    ctx.lineTo(150, y);
    ctx.stroke();
    ctx.textBaseline = "bottom";
    ctx.fillText("Bottom Q", 20, y);
    y += 40 + pad;

    ctx.moveTo(20, y);
    ctx.lineTo(150, y);
    ctx.stroke();
    ctx.textBaseline = "middle";
    ctx.fillText("Middle Q", 20, y);
    y += 40 + pad;

    ctx.moveTo(20, y);
    ctx.lineTo(150, y);
    ctx.stroke();
    ctx.textBaseline = "hanging";
    ctx.fillText("Hanging Q", 20, y);
    y += 40 + pad;
    comparePdf(doc.output(), "textBaseline.pdf", "context2d");
  });

  it("autoPaging with 10 or more pages", () => {
    const doc = new jsPDF({ format: [100, 100], floatPrecision: 2 });
    const ctx = doc.context2d;
    ctx.autoPaging = true;
    ctx.fillStyle = "red";
    ctx.fillRect(0, 750, 20, 100); // rectangle starting from page 8 going to page 9
    ctx.fillStyle = "blue";
    ctx.fillRect(0, 850, 20, 100); // rectangle starting from page 9 going to page 10

    comparePdf(doc.output(), "autoPaging10Pages.pdf", "context2d");
  });

  it("lineWidth should be nonnegative", () => {
    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 3
    });
    var ctx = doc.context2d;
    var writeArray = [];
    doc.__private__.setCustomOutputDestination(writeArray);

    ctx.beginPath();
    ctx.strokeStyle = "#FF0000";
    ctx.transform(-1, 0, 0, 1, 0, 0);
    ctx.moveTo(0, 0);
    ctx.lineTo(100, 100);
    ctx.stroke();

    expect(writeArray).toEqual([
      "1. 0. 0. RG",
      "1. w", // <- should be nonnegative
      "0. 841.89 m",
      "-100. 741.89 l",
      "S",
      "1. w"
    ]);
  });

  it("margin property shorthands", () => {
    const doc = new jsPDF();
    const ctx = doc.context2d;
    expect(ctx.margin).toEqual([0, 0, 0, 0]);
    ctx.margin = 1;
    expect(ctx.margin).toEqual([1, 1, 1, 1]);
    ctx.margin = [1];
    expect(ctx.margin).toEqual([1, 1, 1, 1]);
    ctx.margin = [1, 2];
    expect(ctx.margin).toEqual([1, 2, 1, 2]);
    ctx.margin = [1, 2, 3];
    expect(ctx.margin).toEqual([1, 2, 3, 2]);
    ctx.margin = [1, 2, 3, 4];
    expect(ctx.margin).toEqual([1, 2, 3, 4]);
  });

  it("font face map cache invalidation", () => {
    const ptSans = loadBinaryResource("reference/PTSans.ttf");

    var doc = new jsPDF({
      orientation: "p",
      unit: "pt",
      format: "a4",
      floatPrecision: 2
    });
    doc.addFileToVFS("CustomFont", ptSans);
    doc.addFont("CustomFont", "CustomFont", "normal");

    var ctx = doc.context2d;

    // Set up font faces for context2d
    var fontFaces = [
      {
        family: "CustomFont",
        weight: "normal",
        style: "normal",
        ref: { name: "CustomFont", style: "normal" }
      }
    ];
    ctx.fontFaces = fontFaces;

    // Set a font that uses the font face map
    ctx.font = "12pt CustomFont, Arial, sans-serif";

    // Add a new font to the document (simulating dynamic font loading)
    // This should trigger font face map cache invalidation
    doc.addFileToVFS("NewCustomFont", ptSans);
    doc.addFont("NewCustomFont", "NewCustomFont", "normal");

    // Change font again - this should rebuild the font face map
    // and not use stale cached data
    ctx.font = "12pt NewCustomFont, CustomFont, Arial, sans-serif";

    // If the bug existed, the font face map would not be rebuilt
    // and the new font would not be recognized
    // This test ensures the fix works by not throwing errors
    // and properly handling the font change

    // Test that we can still use the original font face
    ctx.font = "12pt CustomFont, Arial, sans-serif";

    // Draw some text to ensure the font handling works
    ctx.fillText("Font state management test", 20, 20);

    // The test passes if no errors are thrown during font changes
    expect(true).toBe(true);
  });
});
